﻿using YamlDotNet.Serialization.NamingConventions;
using YamlDotNet.Serialization;

namespace Meow.Core;

public class Rd_ShaderDef : Def
{
    public string Name = "";
    public string VertexCodePath = "";
    public string FragmentCodePath = "";
}

public class Rd_Shader : MeowRes
{
    public readonly Rd_ShaderDef Def;
    uint programID = 0;

    string vertCode = "";
    string fragCode = "";
    bool _disposed = false;
    public uint ProgramID => programID;
    public bool Disposed => _disposed;

    public Rd_Shader(string defpath, Rd_Context rc)
    {
        Def = new DeserializerBuilder().WithNamingConvention(UnderscoredNamingConvention.Instance).Build().Deserialize<Rd_ShaderDef>(File.ReadAllText(defpath));
        Init(rc);
    }

    public Rd_Shader(Rd_ShaderDef def, Rd_Context rc)
    {
        Def = def;
        Init(rc);
    }

    void Init(Rd_Context rc)
    {
        if (ReadShader(Def.VertexCodePath, ref vertCode) && ReadShader(Def.FragmentCodePath, ref fragCode))
        {
            // loaded shader
            uint vertShader = 0;
            uint fragShader = 0;
            if (rc.CreateShader(vertCode, Silk.NET.OpenGL.ShaderType.VertexShader, ref vertShader) &&
                rc.CreateShader(fragCode, Silk.NET.OpenGL.ShaderType.FragmentShader, ref fragShader) &&
                rc.CreateShaderProgram(vertShader, fragShader, ref programID))
            {
                rc.DeleteShader(vertShader);
                rc.DeleteShader(fragShader);
            }
            else
            {
                Logger.Error("Create Shader Failed: " + Def.Name);
            }

        }
        else
        {
            Logger.Error("Can't Read Shader Code:" + Def.Name);
        }
    }

    


    public static bool ReadShader(string path, ref string shaderCode)
    {
        if (!Path.Exists(path))
        {
            Logger.Error("Can't find shader code at " + path);
            return false;
        }

        var lines = File.ReadAllLines(path);

#if DEBUG
        Logger.Log("Try Read Shader: " + path);
#endif

        shaderCode = "";
        for (var i = 0; i < lines.Length; i++)
        {
            CheckShaderInclude(ref lines[i], path);

            shaderCode += lines[i] + '\n';
        }
        shaderCode = shaderCode.AsciiOnly();

#if DEBUG
        Logger.Log("Shader " + path + ": \n" + shaderCode);
#endif
        return true;
    }

    static void CheckShaderInclude(ref string line, string parentPath)
    {
        if (line.StartsWith(@"#include"))
        {
            // include 的子Shader所用的地址是相对于父级Shader的相对地址
            var path = Path.Combine(
                Path.GetDirectoryName(parentPath),
                line.CleanComment().Split("\"")[1].Trim()
            );

#if DEBUG
            Logger.Log("Try Include " + path);
#endif
            ReadShader(path, ref line);
        }
    }

    public override bool Save(string path)
    {
        return Def.Save(path);
    }

    public void Dispose(Rd_Context rc)
    {
        _disposed = true;
        rc.DeleteProgram(programID);
    }
}
